package org.infodavid.common.persistence;

import java.io.Serializable;
import java.sql.Connection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.persistence.PersistenceException;
import javax.persistence.criteria.CriteriaBuilder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.infodavid.common.persistence.ILockable.ELockType;
import org.infodavid.common.util.IVisitor;

/**
 * the Class PersistenceSession.
 */
@SuppressWarnings("rawtypes")
public abstract class PersistenceSession {

  /**
   * Contains.
   * @param parameters the parameters
   * @param types the types
   * @return true, if successful
   */
  public static boolean contains(final List<IQueryParameter> parameters,
      final IQueryParameter.EParameterType... types) {
    boolean result = false;

    if (!(parameters == null || parameters.isEmpty() || types == null || types.length == 0)) {
      final Iterator<IQueryParameter> ite = parameters.iterator();
      IQueryParameter item;

      while (ite.hasNext() && !result) {
        item = ite.next();

        result = !(item.getType() == null || Arrays.binarySearch(types, item.getType()) < 0);
      }
    }

    return result;
  }

  /**
   * the Class TransactionWrapper.
   */
  @SuppressWarnings("synthetic-access")
  protected static class TransactionWrapper implements ITransaction {

    /** the delegate. */
    private ITransaction delegate;

    /**
     * the Constructor.
     */
    public TransactionWrapper() {
      super();
    }

    /**
     * Begin.
     * @throws PersistenceException the persistence exception
     * @see org.infodavid.common.persistence.ITransaction#begin()
     */
    @Override
    public void begin() throws PersistenceException {
      final long start = System.currentTimeMillis();

      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Starting transaction...");
      }

      if (isActive()) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Transaction already active...");
        }
      }
      else {
        getDelegate().begin();
      }

      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("started in (ms): " + (System.currentTimeMillis() - start));
      }
    }

    /**
     * Commit.
     * @throws PersistenceException the persistence exception
     * @see org.infodavid.common.persistence.ITransaction#commit()
     */
    @Override
    public void commit() throws PersistenceException {
      final long start = System.currentTimeMillis();

      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Commiting transaction...");
      }

      getDelegate().commit();

      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
      }
    }

    /**
     * Gets the delegate.
     * @return the delegate
     */
    public ITransaction getDelegate() {
      return delegate;
    }

    /**
     * Checks if is active.
     * @return true, if checks if is active
     * @throws PersistenceException the persistence exception
     * @see org.infodavid.common.persistence.ITransaction#isActive()
     */
    @Override
    public boolean isActive() throws PersistenceException {
      return getDelegate().isActive();
    }

    /**
     * Rollback.
     * @throws PersistenceException the persistence exception
     * @see org.infodavid.common.persistence.ITransaction#rollback()
     */
    @Override
    public void rollback() throws PersistenceException {
      final long start = System.currentTimeMillis();

      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Rolling back transaction...");
      }

      getDelegate().rollback();

      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
      }
    }

    /**
     * Sets the delegate.
     * @param pDelegate the delegate
     */
    public void setDelegate(final ITransaction pDelegate) {
      if (pDelegate == null) {
        throw new IllegalArgumentException();
      }

      delegate = pDelegate;
    }
  }

  /**
   * the Enum LockMode.
   * @author Copyright PDF Solutions @2009. All rights reserved.
   */
  enum LockMode {

    /** the N o_ wait. */
    NO_WAIT,

    /** the WAIT. */
    WAIT
  }

  /** logger. */
  private static final Log LOGGER = LogFactory.getLog(PersistenceSession.class);

  /** The Constant DEFAULT_FETCH_SIZE. */
  public static final int DEFAULT_FETCH_SIZE = 100;

  /** the transaction wrapper for status update. */
  private final transient TransactionWrapper transactionWrapper;

  /** the provider. */
  private final transient IBasicPersistenceProvider provider;

  /** The is debug enabled. */
  private final boolean isDebugEnabled = LOGGER.isDebugEnabled();

  /**
   * Invoke the 'initialize' method to initialize the new client and the new session.
   * @param arg0 the provider
   * @throws PersistenceException the exception
   */
  protected PersistenceSession(final IBasicPersistenceProvider arg0) throws PersistenceException {
    super();

    provider = arg0;
    transactionWrapper = new TransactionWrapper();

    initialize();
  }

  /**
   * Close.
   * @throws PersistenceException the persistence exception
   */
  public final void close() throws PersistenceException {
    final long start = System.currentTimeMillis();

    if (isDebugEnabled) {
      LOGGER.debug("Closing provider...");
    }

    doClose();

    if (isDebugEnabled) {
      LOGGER.debug("closed in (ms): " + (System.currentTimeMillis() - start));
    }
  }

  /**
   * find.
   * @param <T> the generic type
   * @param clazz the class of the result(s)
   * @param queryName the query name
   * @param parameters the parameters
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T> Object executeNamedQuery(final Class<T> clazz, final String queryName,
      final List<IQueryParameter> parameters) throws PersistenceException {
    return executeQuery(clazz, queryName, parameters, true);
  }

  /**
   * find.
   * @param <T> the generic type
   * @param clazz the class of the result(s)
   * @param query the query
   * @param parameters the parameters
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T> Object executeQuery(final Class<T> clazz,
      final String query, final List<IQueryParameter> parameters) throws PersistenceException {
    return executeQuery(clazz, query, parameters, true);
  }

  /**
   * find.
   * @param <T> the generic type
   * @param clazz the class of the result(s)
   * @param queryName the query name
   * @param parameters the parameters
   * @param single true to get one result, false to get a list of results
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T> Object executeNamedQuery(final Class<T> clazz,
      final String queryName, final List<IQueryParameter> parameters, final boolean single)
      throws PersistenceException {
    final long start = System.currentTimeMillis();
    Object result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Executing using query: " + queryName + ", class: " + clazz + ", parameters: "
          + parameters);
    }

    if (queryName == null) {
      throw new IllegalArgumentException("Query name is null");
    }

    flush();

    result = doExecuteNamedQuery(clazz, queryName, parameters, single);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * find.
   * @param <T> the generic type
   * @param clazz the class of the result(s)
   * @param query the query
   * @param parameters the parameters
   * @param single true to get one result, false to get a list of results
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T> Object executeQuery(final Class<T> clazz, final String query,
      final List<IQueryParameter> parameters, final boolean single) throws PersistenceException {
    final long start = System.currentTimeMillis();
    Object result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Executing using query: " + query + ", class: " + clazz + ", parameters: "
          + parameters);
    }

    if (query == null) {
      throw new IllegalArgumentException("Query name is null");
    }

    flush();

    result = doExecuteQuery(clazz, query, parameters, single);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Find entities.
   * @param <T> the generic type
   * @param clazz the class
   * @param queryName the query name
   * @param parameters the parameters
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> List<T> findEntitiesWithNamedQuery(final Class<T> clazz,
      final String queryName, final List<IQueryParameter> parameters) throws PersistenceException {
    final long start = System.currentTimeMillis();
    List<T> result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Searching using query: " + queryName + ", class: " + clazz + ", parameters: "
          + parameters);
    }

    if (queryName == null) {
      throw new IllegalArgumentException("Query name is null");
    }

    result = doFindEntities(clazz, queryName, parameters);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Find entities.
   * @param <T> the generic type
   * @param clazz the class
   * @param query the query
   * @param parameters the parameters
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> List<T> findEntities(final Class<T> clazz,
      final String query, final List<IQueryParameter> parameters) throws PersistenceException {
    final long start = System.currentTimeMillis();
    List<T> result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Searching using query: " + query + ", class: " + clazz + ", parameters: "
          + parameters);
    }

    if (query == null) {
      throw new IllegalArgumentException("Query name is null");
    }

    result = doFindEntities(clazz, query, parameters);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Find entities.
   * @param <T> the generic type
   * @param clazz the class
   * @param queryName the query name
   * @param key the key
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> T findEntityWithNamedQuery(final Class<T> clazz,
      final String queryName, final Serializable key) throws PersistenceException {
    final long start = System.currentTimeMillis();
    T result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Searching using query: " + queryName + ",  key: " + key + ", class: " + clazz);
    }

    result = doFindEntityWithNamedQuery(clazz, queryName, key, null);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Find entities.
   * @param <T> the generic type
   * @param clazz the class
   * @param key the key
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> T findEntity(final Class<T> clazz, final Serializable key)
      throws PersistenceException {
    final long start = System.currentTimeMillis();
    T result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Searching using key: " + key + ", class: " + clazz);
    }

    result = doFindEntity(clazz, key, null);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Find entities.
   * @param <T> the generic type
   * @param clazz the class
   * @param queryName the query name
   * @param key the key
   * @param type the type of locking
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> T findEntity(final Class<T> clazz,
      final Serializable key, final ELockType type) throws PersistenceException {
    final long start = System.currentTimeMillis();
    T result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Searching using key: " + key + ", class: " + clazz);
    }

    LOGGER.info("Retrieving: " + clazz.getSimpleName() + "(" + key
        + ") with locking using lock type: " + type);

    result = doFindEntity(clazz, key, type);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Get the delegate.
   * @return the delegate
   * @throws PersistenceException the persistence exception
   */
  public abstract Object getDelegate() throws PersistenceException;

  /**
   * Get the connection.
   * @return the connection
   * @throws PersistenceException the persistence exception
   */
  public abstract Connection getConnection() throws PersistenceException;

  /**
   * Get the provider.
   * @return the provider
   */
  public IBasicPersistenceProvider getProvider() {
    return provider;
  }

  /**
   * Gets the transaction.
   * @return the transaction
   * @throws PersistenceException the persistence exception
   */
  public final synchronized ITransaction getTransaction() throws PersistenceException {
    transactionWrapper.setDelegate(doGetTransaction());

    return transactionWrapper;
  }

  /**
   * Insert entities.
   * @param <T> the generic type
   * @param entities the entities
   * @return the collection of inserted entities
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> List<T> insertEntities(final Collection<T> entities)
      throws PersistenceException {
    return insertEntities(entities, null);
  }

  /**
   * Insert entities.
   * @param <T> the generic type
   * @param entities the entities
   * @param visitor the visitor
   * @return the collection of inserted entities
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> List<T> insertEntities(final Collection<T> entities,
      final IVisitor<T,Object> visitor) throws PersistenceException {
    final long start = System.currentTimeMillis();
    List<T> result = null;

    if (isDebugEnabled) {
      for (final T item : entities) {
        LOGGER.debug("Entity to insert: " + item);
      }
    }

    result = doInsertEntities(entities, visitor);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Insert entity.
   * @param <T> the generic type
   * @param entity the entity
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> T insertEntity(final T entity)
      throws PersistenceException {
    final long start = System.currentTimeMillis();
    T result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Inserting using entity: " + entity);
    }

    result = doInsertEntity(entity);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Checks if is open.
   * @return true, if checks if is open
   * @throws PersistenceException the persistence exception
   */
  public abstract boolean isOpen() throws PersistenceException;

  /**
   * Checks if transaction is active.
   * @return true if active
   */
  public boolean isTransactionActive() {
    boolean active = false;

    try {
      final ITransaction transaction = getTransaction();

      active = transaction != null && transaction.isActive();
    }
    catch (final Exception e) {
      LOGGER.warn("An error occurs while checking if transaction is active", e);
    }

    return active;
  }

  /**
   * Initialize.
   * @throws PersistenceException the persistence exception
   */
  protected abstract void initialize() throws PersistenceException;

  /**
   * Handle .
   * @param retry the retry count
   * @param source the object
   * @param error the error
   * @throws PersistenceException the persistence exception
   */
  protected void handle(final int retry, final Object source, final Throwable error)
      throws PersistenceException {
    getProvider().getErrorHandler().handle(null, source, error);
  }

  /**
   * Lock.
   * @param lockable the lockable object
   * @param type the type of locking
   * @throws PersistenceException the persistence exception
   */
  public final void lock(final ILockable lockable, final ELockType type)
      throws PersistenceException {
    final long start = System.currentTimeMillis();

    if (isDebugEnabled) {
      LOGGER.debug("Locking using lockable: " + lockable + ", type: " + type);
    }

    if (lockable instanceof IPersistentData) {
      LOGGER.info("Locking: " + lockable.getClass().getSimpleName() + " ("
          + ((IPersistentData)lockable).getKey() + ") using lock type: " + type);
    }
    else {
      LOGGER.info("Locking: " + lockable + " using lock type: " + type);
    }

    doLock(lockable, type);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }
  }

  /**
   * Removes the entities.
   * @param <T> the generic type
   * @param entities the entities
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> void removeEntities(final Collection<T> entities)
      throws PersistenceException {
    removeEntities(entities, null);
  }

  /**
   * Removes the entities.
   * @param <T> the generic type
   * @param entities the entities
   * @param visitor the visitor
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> void removeEntities(final Collection<T> entities,
      final IVisitor<T,Object> visitor) throws PersistenceException {
    final long start = System.currentTimeMillis();

    if (isDebugEnabled) {
      for (final T item : entities) {
        LOGGER.debug("Data to remove: " + item);
      }
    }

    doRemoveEntities(entities, visitor);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }
  }

  /**
   * Removes the entities.
   * @param <K> the key type
   * @param <T> the generic type
   * @param clazz the class
   * @param keys the keys
   * @param visitor the visitor
   * @throws PersistenceException the persistence exception
   */
  public final <K extends Serializable,T extends IPersistentData<K>> void remove(
      final Class<T> clazz, final Collection<K> keys, final IVisitor<K,Object> visitor)
      throws PersistenceException {
    final long start = System.currentTimeMillis();

    if (isDebugEnabled) {
      for (final K item : keys) {
        LOGGER.debug("Data to remove: " + item);
      }
    }

    doRemove(clazz, keys, visitor);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }
  }

  /**
   * Removes the entity.
   * @param <K> the key type
   * @param <T> the generic type
   * @param clazz the class
   * @param key the key
   * @throws PersistenceException the persistence exception
   */
  public final <K extends Serializable,T extends IPersistentData<K>> void remove(
      final Class<T> clazz, final K key) throws PersistenceException {
    final long start = System.currentTimeMillis();

    if (isDebugEnabled) {
      LOGGER.debug("Removing using key: " + key);
    }

    doRemove(clazz, key);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }
  }

  /**
   * Removes the entity.
   * @param <T> the generic type
   * @param entity the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> void removeEntity(final T entity)
      throws PersistenceException {
    final long start = System.currentTimeMillis();

    if (isDebugEnabled) {
      LOGGER.debug("Removing using entity: " + entity);
    }

    doRemoveEntity(entity);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }
  }

  /**
   * Update entities.
   * @param <T> the generic type
   * @param entities the entities
   * @return the collection of entities
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> Collection<T> updateEntities(final Collection<T> entities)
      throws PersistenceException {
    return updateEntities(entities, null);
  }

  /**
   * Update entities.
   * @param <T> the generic type
   * @param entities the entities
   * @param visitor the visitor
   * @return the collection of entities
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> Collection<T> updateEntities(
      final Collection<T> entities, final IVisitor<T,Object> visitor) throws PersistenceException {
    final long start = System.currentTimeMillis();
    Collection<T> result = null;

    if (isDebugEnabled) {
      for (final T item : entities) {
        LOGGER.debug("Data to update: " + item);
      }
    }

    result = doUpdateEntities(entities, visitor);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Update entity.
   * @param <T> the generic type
   * @param entity the entity
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  public final <T extends IPersistentData> T updateEntity(final T entity)
      throws PersistenceException {
    final long start = System.currentTimeMillis();
    T result = null;

    if (isDebugEnabled) {
      LOGGER.debug("Updating using entity: " + entity);
    }

    result = doUpdateEntity(entity);

    if (isDebugEnabled) {
      LOGGER.debug("done in (ms): " + (System.currentTimeMillis() - start));
    }

    return result;
  }

  /**
   * Close.
   * @throws PersistenceException the persistence exception
   */
  protected abstract void doClose() throws PersistenceException;

  /**
   * Gets the criteria builder.
   * @return the criteria builder
   * @throws PersistenceException the persistence exception
   */
  public abstract CriteriaBuilder getCriteriaBuilder() throws PersistenceException;

  /**
   * Execute.
   * @param <T> the generic type
   * @param clazz the entity class
   * @param queryName the query name
   * @param parameters the parameters
   * @param single true to get one result, false to get a list of results
   * @return the result
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T> Object doExecuteNamedQuery(Class<T> clazz, String queryName,
      List<IQueryParameter> parameters, boolean single) throws PersistenceException;

  /**
   * Execute.
   * @param <T> the generic type
   * @param clazz the entity class
   * @param query the query
   * @param parameters the parameters
   * @param single true to get one result, false to get a list of results
   * @return the result
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T> Object doExecuteQuery(Class<T> clazz, String query,
      List<IQueryParameter> parameters, boolean single) throws PersistenceException;

  /**
   * Find entities.
   * @param <T> the generic type
   * @param entityClass the entity class
   * @param queryName the query name
   * @param parameters the parameters
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> List<T> doFindEntitiesWithNamedQuery(
      Class<T> entityClass, String queryName, List<IQueryParameter> parameters)
      throws PersistenceException;

  /**
   * Find entities.
   * @param <T> the generic type
   * @param entityClass the entity class
   * @param query the query
   * @param parameters the parameters
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> List<T> doFindEntities(Class<T> entityClass,
      String query, List<IQueryParameter> parameters) throws PersistenceException;

  /**
   * Find entities.
   * @param <T> the generic type
   * @param entityClass the entity class
   * @param queryName the query name
   * @param key the key ILockable.ELockType type
   * @param lockType the lock type
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> T doFindEntityWithNamedQuery(Class<T> entityClass,
      String queryName, Serializable key, ELockType lockType) throws PersistenceException;

  /**
   * Find entities.
   * @param <T> the generic type
   * @param entityClass the entity class
   * @param key the key ILockable.ELockType type
   * @param lockType the lock type
   * @return the entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> T doFindEntity(Class<T> entityClass,
      Serializable key, ELockType lockType) throws PersistenceException;

  /**
   * Gets the transaction.
   * @return the transaction
   * @throws PersistenceException the persistence exception
   */
  protected abstract ITransaction doGetTransaction() throws PersistenceException;

  /**
   * Insert entities.
   * @param <T> the generic type
   * @param entities the entities
   * @param visitor the visitor
   * @return the collection of inserted entities
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> List<T> doInsertEntities(Collection<T> entities,
      IVisitor<T,Object> visitor) throws PersistenceException;

  /**
   * Insert entity.
   * @param <T> the generic type
   * @param entity the entity
   * @return the inserted entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> T doInsertEntity(T entity)
      throws PersistenceException;

  /**
   * Lock.
   * @param lockable the lockable object
   * @param type the type of locking
   * @throws PersistenceException the persistence exception
   */
  protected abstract void doLock(ILockable lockable, ELockType type) throws PersistenceException;

  /**
   * Removes the entities.
   * @param <K> the key type
   * @param <T> the generic type
   * @param entityClass the entity class
   * @param keys the keys
   * @param visitor the visitor
   * @throws PersistenceException the persistence exception
   */
  protected abstract <K extends Serializable,T extends IPersistentData<K>> void doRemove(
      Class<T> entityClass, Collection<K> keys, IVisitor<K,Object> visitor)
      throws PersistenceException;

  /**
   * Removes the entity.
   * @param <K> the key type
   * @param <T> the generic type
   * @param entityClass the entity class
   * @param key the key
   * @throws PersistenceException the persistence exception
   */
  protected abstract <K extends Serializable,T extends IPersistentData<K>> void doRemove(
      Class<T> entityClass, K key) throws PersistenceException;

  /**
   * Removes the entities.
   * @param <T> the generic type
   * @param entities the entities
   * @param visitor the visitor
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> void doRemoveEntities(Collection<T> entities,
      IVisitor<T,Object> visitor) throws PersistenceException;

  /**
   * Removes the entity.
   * @param <T> the generic type
   * @param entity the entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> void doRemoveEntity(T entity)
      throws PersistenceException;

  /**
   * Update entities.
   * @param <T> the generic type
   * @param entities the entities
   * @param visitor the visitor
   * @return the collection of updated entities
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> Collection<T> doUpdateEntities(
      Collection<T> entities, IVisitor<T,Object> visitor) throws PersistenceException;

  /**
   * Update entity.
   * @param <T> the generic type
   * @param entity the entity
   * @return the updated entity
   * @throws PersistenceException the persistence exception
   */
  protected abstract <T extends IPersistentData> T doUpdateEntity(T entity)
      throws PersistenceException;

  /**
   * Flush.
   * @throws PersistenceException the persistence exception
   */
  public abstract void flush() throws PersistenceException;
}
